#!/bin/sh -ef
#
# Copyright (C) 2003-2019  Dmitry V. Levin <ldv@altlinux.org>
# Copyright (C) 2006  Alexey Gladkov <legion@altlinux.org>
# Copyright (C) 2007  Alex V. Myltsev <avm@altlinux.org>
# 
# The mkaptbox utility for the hasher project
#
# SPDX-License-Identifier: GPL-2.0-or-later
#

. hsh-sh-functions

show_help()
{
	cat <<EOF
mkaptbox - create initial aptbox in specified working directory.

Usage: $PROG [options] [<path-to-workdir>]

<path-to-workdir> must be valid writable directory.

Options:
  --apt-config=FILE         path to custom apt.conf file;
  --apt-prefix=DIR          path to apt directory prefix (e.g. /usr);
  -f, --force               force aptbox creation;
  --no-update               do not run "apt-get update" after creation;
  --repo=DIR                path to work repository directory;
  --repo-bin=DIR            binary packages destination directory,
                            overriding --repo option for binary packages;
  --repo-src=DIR            source packages destination directory,
                            overriding --repo option for source packages;
  --target=ARCH             target architecture of the work repository;
  -u, --update              run "apt-get update" after creation;
  --wait-lock               wait for workdir lock;
  --no-wait-lock            do not wait for workdir lock;
  --without-stuff           do not configure sources.list for the work repository;
  --with-stuff              configure sources.list for the work repository;
  -q, --quiet               try to be more quiet;
  -v, --verbose             print a message for each action;
  -V, --version             print program version and exit;
  -h, --help                show this text and exit.

Report bugs to http://bugs.altlinux.ru/

EOF
	exit
}

TEMP=`getopt -n $PROG -o f,u,$getopt_common_opts -l apt-config:,apt-prefix:,force,no-lock,no-stuff,no-update,repo:,repo-bin:,repo-src:,target:,update,wait-lock,no-wait-lock,without-stuff,with-stuff,$getopt_common_longopts -- "$@"` ||
	show_usage
eval set -- "$TEMP"

force=
update=1
while :; do
	case "$1" in
		--apt-config)
			apt_config="$(opt_check_read "$1" "$2")"
			shift
			;;
		--apt-prefix)
			apt_prefix="$(opt_check_dir "$1" "$2")"
			shift
			;;
		-f|--force) force=1
			;;
		--no-lock) no_lock=1
			;;
		--no-update) update=
			;;
		--repo) shift; repo="$1"
			;;
		--repo-bin) shift; repo_bin="$1"
			;;
		--repo-src) shift; repo_src="$1"
			;;
		--target) shift; target="$1"
			[ -z "${target##[A-Za-z]*}" ] ||
				fatal "--target: $target: invalid architecture."
			;;
		-u|--update) update=1
			;;
		--wait-lock) lock_nowait=
			;;
		--no-wait-lock) lock_nowait=1
			;;
		--without-stuff|--no-stuff) no_stuff=1
			;;
		--with-stuff) no_stuff=
			;;
		--) shift; break
			;;
		*) parse_common_option "$1"
			;;
	esac
	shift
done

if [ -z "$workdir" ]; then
	# Exactly one argument.
	[ "$#" -ge 1 ] || show_usage 'Insufficient arguments.'
	[ "$#" -le 1 ] || show_usage 'Too many arguments.'
else
	# At most one argument.
	[ "$#" -le 1 ] || show_usage 'Too many arguments.'
fi

if [ "$#" -ge 1 ]; then
	set_workdir "$1"
	shift
else
	set_workdir
fi

lock_workdir

[ -n "$no_stuff" ] ||
	make_repo
[ -n "$repo" ] || repo="$def_repo"
repo="$(readlink -fv -- "$repo")"

# does aptbox exist yet?
if [ -e aptbox -o -L aptbox ]; then
	[ -n "$force" ] || fatal "remove $aptbox first."
	[ -d aptbox ] || fatal "$aptbox: invalid pathname."

	# empty dir?
	if rmdir -- aptbox 2>/dev/null; then
		mkdir -m700 $verbose -- aptbox >&2
		cd aptbox
	else
		cd aptbox
		[ -d ./etc/apt -a -d ./var/lib/rpm ] ||
			fatal "$aptbox: doesn't look valid."
	fi
else
	mkdir -m700 $verbose -- aptbox >&2
	cd aptbox
fi

verbose "Changed working directory to \`$aptbox'."

if [ -n "$unchecked_initroot_cache" ]; then
	cookie_file="$cache_dir/chroot/cookie"
	compressor_used="$cache_dir/chroot/compressor"
	aptbox_archive="$cache_dir/chroot/aptbox.tar"

	if [ -s "$cookie_file" -a -s "$compressor_used" -a -s "$aptbox_archive" ] &&
	   [ "$unchecked_initroot_cache" = "$(cat -- "$cookie_file")" ]; then
		compressor_prog=$(cat "$compressor_used")
		$compressor_prog -d < "$aptbox_archive" |
		tar -x &&
			verbose "Unpacked $aptbox_archive." ||
			fatal "Unpack of $aptbox_archive failed."
		exit 0
	fi
fi

# create apt/rpm skeleton directories.
mkdir -p $verbose -- \
	./etc/apt/{apt.conf,{sources,vendors}.list}.d \
	./home/user \
	./var/cache/apt/archives/partial \
	./var/lib/apt/lists/partial \
	./var/lib/rpm >&2

# define apt variables.
prog_apt_get="apt-get"
prog_apt_cache="apt-cache"
prog_apt_config="apt-config"
apt_libdir=
if [ -z "$apt_prefix" ]; then
	apt_libdir="$LD_LIBRARY_PATH"
else
	[ -d "$apt_prefix" ] ||
		fatal "$apt_prefix: directory not available."

	if printf %s "$apt_prefix" |LC_ALL=C grep -qs '[`"$\]'; then
		fatal "$apt_prefix: illegal symbols in apt prefix."
	fi

	slibdir="$(getconf SLIB ||:)"
	[ -n slibdir ] || slibdir="lib"

	apt_libdir="$apt_prefix/$slibdir"
	[ -d "$apt_libdir" ] ||
		fatal "$apt_libdir: directory not available."

	if [ -n "$LD_LIBRARY_PATH" ]; then
		apt_libdir="$LD_LIBRARY_PATH:$apt_libdir"
	fi

	for n in apt-cache apt-config apt-get; do
		prog="$apt_prefix/bin/$n"
		[ -x "$prog" ] ||
			fatal "$prog: program not available."
		var="$(printf 'prog_%s' "$n" |tr - _)"
		eval "$var=\"$(quote_shell "$prog")\""
	done
fi
if printf %s "$apt_libdir" |LC_ALL=C grep -qs '[`"$\]'; then
	fatal "$LD_LIBRARY_PATH: illegal symbols in LD_LIBRARY_PATH."
fi

# Create setarch wrapper.
setarch=
if setarch "${target:-$def_target}" true 2>/dev/null; then
	setarch="setarch \"$(quote_shell "${target:-$def_target}")\""
fi
cat >setarch <<__EOF__
#!/bin/sh -e
exec $setarch "\$@"
__EOF__
chmod $verbose a+x setarch >&2

# create aptbox version of ~/.rpmrc if necessary.
aptbox_home=
rpm_showrc="$("$aptbox/setarch" rpm --showrc)"
current_arch="$(printf %s "$rpm_showrc" |sed -ne 's/^install arch[[:space:]]*:[[:space:]]*\([^[:space:]]\+\).*/\1/p')"
[ -n "$current_arch" ] ||
	current_arch="$(uname -m)"
[ "$current_arch" != "${target:-$def_target}" ] &&
	alternate_arch=1 ||
	alternate_arch=
if [ -n "$alternate_arch" ] &&
   ! printf %s "$rpm_showrc" |
     LC_ALL=C grep -qs "^compatible archs[[:space:]]*:.*\<${target:-$def_target}\>"; then
	aptbox_home="$aptbox/home/user"
	for f in "$HOME/.rpmrc" "$HOME/.rpmmacros"; do
		[ ! -f "$f" ] ||
			install -pm600 $verbose -- "$f" "$aptbox_home/" >&2
	done
	echo "arch_compat: $current_arch: ${target:-$def_target}" >> "$aptbox_home/.rpmrc"
fi

# Create apt wrappers.
apt_config_file="$aptbox/etc/apt/apt.conf"
for n in apt-cache apt-config apt-get; do
	cat >"$n" <<__EOF__
#!/bin/sh -e
${apt_libdir:+export LD_LIBRARY_PATH="$apt_libdir"}
APT_CONFIG="$apt_config_file" \
${aptbox_home:+HOME="$aptbox_home"} \
exec $aptbox/setarch \
$(eval printf %s "$(printf '$prog_%s' "$n" |tr - _)") "\$@"
__EOF__
	verbose "created $n wrapper"
	chmod $verbose a+x "$n" >&2
done

cat >regenbasedir <<__EOF__
#!/bin/sh -ef
# Note that hasher uses rpm-dir method instead of genbasedir.
# Only use this script e.g. before uploading your repo to ftp.
mkdir -p "$repo"/${target:-$def_target}/base
genbasedir --topdir="$repo" --no-oldhashfile --bz2only --mapi --bloat ${target:-$def_target} hasher "\$@"
__EOF__
verbose "created regenbasedir"
chmod $verbose a+x regenbasedir >&2

# create initial apt.conf file.
cat >"$apt_config_file" <<__EOF__
${apt_config:+$(cat "$apt_config")}
__EOF__

get_apt_config Dir::Etc::SourceList/f sl_value
[ -z "${sl_value##/*}" ] &&
	[ -r "$sl_value" -a -s "$sl_value" ] ||
	fatal "apt-config: broken Dir::Etc::SourceList: $sl_value"

# if cdroms.list is not empty, copy it and apt indices into aptbox
get_apt_config Dir::State::cdroms/f cd_value
if [ -r "$cd_value" -a -s "$cd_value" ]; then
	install -p -m644 $verbose "$cd_value" "$aptbox/var/lib/apt/" >&2

	get_apt_config Dir::State::lists/d lists_value
	if [ -r "$lists_value" -a -d "$lists_value" ]; then
		find "$lists_value/" -mindepth 1 -maxdepth 1 -perm -444 -type f -exec \
			install -p -m644 $verbose '{}' "$aptbox/var/lib/apt/lists/" ';' >&2
	fi
fi

# if preferences file is specified and not empty, copy it into aptbox either
get_apt_config Dir::Etc::preferences/f prefs_value
if [ -r "$prefs_value" -a -s "$prefs_value" ]; then
	install -p -m644 $verbose "$prefs_value" "$aptbox/etc/apt/preferences" >&2
else
	prefs_value=
fi
get_apt_config Dir::Etc::preferencesparts/d prefsdir_value
if [ -r "$prefsdir_value" -a -d "$prefsdir_value" ]; then
	mkdir -p "$aptbox/etc/apt/preferences.d"
	find "$prefsdir_value/" -mindepth 1 -maxdepth 1 -perm -444 -type f -exec \
		install -p -m644 $verbose '{}' "$aptbox/etc/apt/preferences.d/" ';' >&2
else
	prefsdir_value=
fi

# create final apt.conf file.
cat >"$apt_config_file" <<__EOF__
Dir::State "$aptbox/var/lib/apt/";
Dir::Cache "$aptbox/var/cache/apt/";
${apt_config:+$(cat "$apt_config")}
Dir::Etc::SourceList "$aptbox/etc/apt/sources.list";
Dir::Etc::preferences "$aptbox/etc/apt/preferences";
Dir::Etc::preferencesparts "$aptbox/etc/apt/preferences.d";
RPM::RootDir "$aptbox";
APT::Install::Virtual "true";
APT::Install::VirtualVersion "true";
${alternate_arch:+APT::Architecture "${target:-$def_target}";}
__EOF__

verbose "Created APT configuration file \`$apt_config_file'."

# create a new sources.list file.
target_config="./etc/apt/sources.list"
cat >"$target_config" <<__EOF__
${no_stuff:+#}rpm-dir file:$repo ${target:-$def_target} hasher
$(LC_ALL=C grep '^[^#]' "$sl_value")
__EOF__
verbose "Created APT source list file \`$target_config'."

# create a new rpm database.
"$aptbox/setarch" rpmdb --initdb --dbpath "$aptbox/var/lib/rpm"
verbose "Created RPM database in \`./var/lib/rpm/'."

[ -z "$update" ] || "$aptbox/apt-get" update $([ -n "$verbose" ] || echo -qq)
